package com.bzframework.project.api.service;

import com.bzframework.pro.api.core.encipher.OpenApiClient;
import com.bzframework.pro.api.core.manager.ApiSupportManager;
import com.bzframework.pro.api.core.provider.ApiSupport;
import com.bzframework.project.api.configuration.ProtocolProperties;
import com.bzframework.project.api.entity.OpenApiEntity;
import com.bzframework.project.api.enums.OpenApiTypes;
import com.bzframework.project.api.model.request.ApiInvokeRequest;
import com.bzframework.project.api.model.request.RequestBodyOption;
import com.bzframework.project.api.utils.QueryStringUtils;
import lombok.RequiredArgsConstructor;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.bean.FastBeanCopier;
import org.hswebframework.web.crud.service.GenericReactiveCrudService;
import org.hswebframework.web.exception.BusinessException;
import org.hswebframework.web.id.IDGenerator;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author bingzhong
 * @Date 2025/3/3 15:52
 * @Description
 */
@Service
@RequiredArgsConstructor
public class OpenApiService extends GenericReactiveCrudService<OpenApiEntity, String> {

    private final OpenApiClient openApiClient;

    private final ProtocolProperties protocolProperties;


    public Mono<String> uploadApiSupportJar(FilePart filePart) {
        String jarPath = IDGenerator.SNOW_FLAKE_STRING.generate() + ".jar";
        Path apiStoragePath = Paths.get(protocolProperties.getBaseStoragePath(), jarPath);
        apiStoragePath.getParent().toFile().mkdir();
        return DataBufferUtils.write(filePart.content(), apiStoragePath,
                                     StandardOpenOption.CREATE, StandardOpenOption.WRITE)
                              .thenReturn(jarPath);
    }

    public Mono<String> apiInvoke(ApiInvokeRequest apiInvokeRequest) {

        String url = apiInvokeRequest.getUrl();

        // 构建请求头
        List<ApiInvokeRequest.NameValueField> requestHeaderList = apiInvokeRequest.getRequestHeader();
        Map<String, List<String>> requestHeaderMap = requestHeaderList
                .stream()
                .collect(Collectors.toMap(ApiInvokeRequest.NameValueField::getName, e -> Collections.singletonList(e.getValue())));
        requestHeaderMap.putAll(openApiClient.getSignHeaderMapValueList());
        MultiValueMap<String, String> requestHeader = CollectionUtils.toMultiValueMap(requestHeaderMap);

        // 构建请求参数
        List<ApiInvokeRequest.NameValueField> requestParamList = apiInvokeRequest.getRequestParam();
        Map<String, ApiInvokeRequest.NameValueField> requestParamMap = requestParamList
                .stream()
                .collect(Collectors.toMap(ApiInvokeRequest.NameValueField::getKey, e -> e));

        // query param 拼接路径
        url = QueryStringUtils.buildQueryString(url, requestParamMap);

        Integer timeout = apiInvokeRequest.getTimeout();
        RequestBodyOption requestBodyOption = apiInvokeRequest.getRequestBodyOption();
        Object requestBody = requestBodyOption.getRequestBody();
        return WebClient.create(url)
                        .method(Objects.requireNonNull(HttpMethod.resolve(apiInvokeRequest.getMethod().toUpperCase())))
                        .headers(httpHeaders -> {
                            httpHeaders.addAll(requestHeader);
                            if (requestBodyOption.getBodyType().equals(RequestBodyOption.BodyTypes.RAW)) {
                                RequestBodyOption.RequestBodyTypes requestBodyType = requestBodyOption.getRequestBodyType();
                                if (RequestBodyOption.RequestBodyTypes.JSON.equals(requestBodyType)) {
                                    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                                }
                            }
                        })
                        .bodyValue(requestBody)
                        .retrieve()
                        .bodyToMono(String.class)
                        .timeout(Duration.ofSeconds(timeout))
                        .onErrorResume(e -> Mono.error(new BusinessException("请求超时")));
    }
}
